#include "SquirrelMain.h"
#include "SquirrelFuncs.h"
#include <stdarg.h>
#include <stdio.h>

void Print( HSQUIRRELVM v, const SQChar *s, ... )
{
	char sz[ 256 ] = {0};
	va_list arglist;
	va_start( arglist, s );
	vsprintf( sz, s, arglist );
	va_end( arglist );

	CallFunc( "echo", "%s", sz );
}

void Error( HSQUIRRELVM v, const SQChar *s, ... )
{
	va_list arglist;
	va_start(arglist, s);
	scvprintf(s, arglist);
	va_end(arglist);
}

CSquirrel::CSquirrel( unsigned char uc, char* sz )
{
	m_ucID = uc;
	strcpy( m_szFilename, sz );

	/*SquirrelVM::Init();
	m_pVM = SquirrelVM::GetVMPtr();
	m_pVMSys = m_pVM;*/
	m_pVM = sq_open( 1024 ); // creates a VM with initial stack size 1024
	if ( m_pVM )
	{
		sq_pushroottable( m_pVM );
		sqstd_dofile( m_pVM, _SC( sz ), 0, 1 );

		sq_setprintfunc( m_pVM, Print, Error );

		sqstd_seterrorhandlers( m_pVM );

		SquirrelVM::InitNoRef( m_pVM );
		m_pVMSys = m_pVM;
		CSquirrelFuncs::RegisterFuncs( m_pVM );
	}

	m_bWaiting = false;
	m_ucParams = 0;
	m_iOrigStack = 0;

	PushFunction( "OnScriptLoad" );
	CallFunction();
}

CSquirrel::~CSquirrel(void)
{
	if ( m_pVM )
	{
		PushFunction( "OnScriptUnload" );
		CallFunction();

		SquirrelVM::SetVMSys( m_pVMSys );
		SquirrelVM::Shutdown();

		sq_pop( m_pVM, 1 );

		sq_close( m_pVM );

		m_pVM = NULL;
		m_pVMSys = NULL;
	}
}

void CSquirrel::PushFunction( char* sz )
{
	if ( m_pVM )
	{
		if ( !m_bWaiting )
		{
			m_iOrigStack = sq_gettop( m_pVM );
			sq_pushroottable( m_pVM );
			sq_pushstring( m_pVM,_SC( sz ), -1 );
			if( SQ_SUCCEEDED( sq_get( m_pVM, -2 ) ) )
			{
				sq_pushroottable( m_pVM );

				m_ucParams++;
				m_bWaiting = true;
			}
			else sq_pop( m_pVM, 1 );
		}
	}
}

void CSquirrel::PushFunction( void* p )
{
	if ( m_pVM )
	{
		if ( !m_bWaiting )
		{
			m_iOrigStack = sq_gettop( m_pVM );
			sq_pushroottable( m_pVM );
			sq_pushuserpointer( m_pVM, (SQUserPointer)p );
			if( SQ_SUCCEEDED( sq_get( m_pVM, -2 ) ) )
			{
				sq_pushroottable( m_pVM );

				m_ucParams++;
				m_bWaiting = true;
			}
			else sq_pop( m_pVM, 1 );
		}
	}
}

void CSquirrel::PushInteger( int i )
{
	if ( m_pVM )
	{
		if ( m_bWaiting )
		{
			sq_pushinteger( m_pVM, i );
			m_ucParams++;
		}
	}
}

void CSquirrel::PushFloat( float f )
{
	if ( m_pVM )
	{
		if ( m_bWaiting )
		{
			sq_pushfloat( m_pVM, f );
			m_ucParams++;
		}
	}
}

void CSquirrel::PushString( char* sz )
{
	if ( m_pVM )
	{
		if ( m_bWaiting )
		{
			if ( strlen( sz ) > 0 ) sq_pushstring( m_pVM, _SC( sz ), -1 );
			else sq_pushnull( m_pVM );

			m_ucParams++;
		}
	}
}

void CSquirrel::PushPointer( void* p )
{
	if ( m_pVM )
	{
		if ( m_bWaiting )
		{
			sq_pushuserpointer( m_pVM, (SQUserPointer)p );

			m_ucParams++;
		}
	}
}

void CSquirrel::PushNull( void )
{
	if ( m_pVM )
	{
		if ( m_bWaiting )
		{
			sq_pushnull( m_pVM );

			m_ucParams++;
		}
	}
}

void CSquirrel::CallFunction( void )
{
	if ( m_pVM )
	{
		if ( m_bWaiting )
		{
			SquirrelVM::SetVMSys( m_pVMSys );
			sq_call( m_pVM, m_ucParams, 0, 0 );
			sq_settop( m_pVM, m_iOrigStack );

			m_ucParams = 0;
			m_bWaiting = false;
		}
	}
}

// The Manager
unsigned char CSquirrelManager::m_ucScripts = 0;
std::vector< CSquirrel* > CSquirrelManager::m_Scripts( 250 );

CSquirrel* CSquirrelManager::New( char* sz )
{
	unsigned char uc = FindFreeID();
	if ( uc < 250 )
	{
		CSquirrel* p = new CSquirrel( uc, sz );
		if ( p )
		{
			m_Scripts[ uc ] = p;

			m_ucScripts++;

			return p;
		}
	}
	return NULL;
}

CSquirrel* CSquirrelManager::New( unsigned char uc, char* sz )
{
	if ( uc < 250 )
	{
		CSquirrel* p = new CSquirrel( uc, sz );
		if ( p )
		{
			m_Scripts[ uc ] = p;

			m_ucScripts++;

			return p;
		}
	}
	return NULL;
}

CSquirrel* CSquirrelManager::Find( unsigned char uc )
{
	if ( uc < 250 ) return m_Scripts[ uc ];

	return NULL;
}

CSquirrel* CSquirrelManager::Find( char* sz )
{
	unsigned char uc = 0, uc1 = 0;
	CSquirrel* pScript = NULL;
	while ( ( uc < 250 ) && ( uc1 < m_ucScripts ) )
	{
		pScript = m_Scripts[ uc ];
		if ( pScript )
		{
			if ( !strcmp( pScript->GetScriptName(), sz ) ) return pScript;

			uc1++;
		}

		uc++;
	}

	return NULL;
}

CSquirrel* CSquirrelManager::Find( HSQUIRRELVM v )
{
	unsigned char uc = 0, uc1 = 0;
	CSquirrel* pScript = NULL;
	while ( ( uc < 250 ) && ( uc1 < m_ucScripts ) )
	{
		pScript = m_Scripts[ uc ];
		if ( pScript )
		{
			if ( pScript->GetVM() == v ) return pScript;

			uc1++;
		}

		uc++;
	}

	return NULL;
}

bool CSquirrelManager::Remove( CSquirrel* p )
{
	if ( p )
	{
		m_Scripts[ p->GetID() ] = NULL;
		delete p;

		m_ucScripts--;

		return true;
	}
	return false;
}

bool CSquirrelManager::Remove( unsigned char uc )
{
	CSquirrel* p = Find( uc );
	if ( p )
	{
		m_Scripts[ uc ] = NULL;
		delete p;

		m_ucScripts--;

		return true;
	}
	return false;
}

bool CSquirrelManager::Remove( char* sz )
{
	CSquirrel* p = Find( sz );
	if ( p )
	{
		m_Scripts[ p->GetID() ] = NULL;
		delete p;

		m_ucScripts--;

		return true;
	}
	return false;
}

void CSquirrelManager::RemoveAll( void )
{
	unsigned char uc = 0, uc1 = 0;
	CSquirrel* pScript = NULL;
	while ( ( uc < 250 ) && ( uc1 < m_ucScripts ) )
	{
		pScript = m_Scripts[ uc ];
		if ( pScript )
		{
			m_Scripts[ uc ] = 0;
			delete pScript;

			uc1++;
		}

		uc++;
	}

	m_ucScripts = 0;
}

unsigned char CSquirrelManager::FindFreeID( void )
{
	for ( unsigned char uc = 0; uc < 250; uc++ )
	{
		if ( !m_Scripts[ uc ] ) return uc;
	}

	return 250;
}